﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Windows.Forms;

namespace FindClone
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            comboBox1.Items.AddRange(AnalyzeSizeAlghoritm.ToCollection());
            var analyzeSizeAlghoritm = ConfigurationManager.AppSettings["AnalyzeSizeAlghoritm"];
            if (analyzeSizeAlghoritm != null)
            {
                comboBox1.SelectedItem = analyzeSizeAlghoritm;
            }
        }

        #region ProgressBar

        private void InitProgressBar(int maximum)
        {
            Invoke(new MethodInvoker(() =>
            {
                progressBarCalculateHash.Minimum = 0;
                progressBarCalculateHash.Maximum = maximum;
                progressBarCalculateHash.Step = 1;
                ResetProgressBar();
            }));
        }

        private void ResetProgressBar()
        {
            Invoke(new MethodInvoker(() =>
            {
                progressBarCalculateHash.Value = progressBarCalculateHash.Minimum;
            }));
        }

        private void IncrementProgressBar()
        {
            Invoke(new MethodInvoker(() => progressBarCalculateHash.PerformStep()));
        }

        #endregion

        private void Analyze(string directoryPath, string searchPattern, string alghoritm)
        {
            var files = DirectoryListing.GetAllFiles(directoryPath, searchPattern)
                                        .GroupBy(f => f.Size) // Определяем дубли по размеру (первично, затем будет детальная проверка по байтам)
                                        .Where(i => i.Count() > 1) // Сохраняем только дубли
                                        .OrderByDescending(fg => fg.Key); //По размеру от наибольшего

            InitProgressBar(files.Count());

            foreach (var filesGroup in files)
            {
                if (_stop) break;

                AnalyzeFileBytes(filesGroup, alghoritm);

                IncrementProgressBar();
            }

            ResetProgressBar();

            CalculateSeletedFilesInfo();
        }

        private readonly ByteEqualityComparer _byteEqualityComparer = new ByteEqualityComparer();

        private void AnalyzeFileBytes(IEnumerable<LocalFile> files, string alghoritm)
        {
            switch (alghoritm)
            {
                case AnalyzeSizeAlghoritm.ByPathDesc:
                    files = files.OrderBy(i => i.Path);
                    break;
                case AnalyzeSizeAlghoritm.ByCreationDateAscThenAccessDateAsc:
                    files = files.OrderBy(i => i.CreationTime) //От самой ранней даты к более новой
                                 .ThenBy(i => i.LastAccessTime); //Затем по дате доступа
                    break;
            }

            var filesGroups = files.GroupBy(i => i.Path, _byteEqualityComparer) //Выбираем одинаковые файлы
                                   .Where(i => i.Count() > 1);

            foreach (var filesGroup in filesGroups)
            {
                var group = filesGroup;
                Invoke(new MethodInvoker(() =>
                                             {
                                                 var firstFile = group.First();

                                                 var headerStr = String.Format(Localization.RepeatFile, firstFile.Size.ToFileSize());
                                                 textBoxLog.WriteToLog(headerStr, Utility.MessageType.SubMain);
                                                 
                                                 var repeatFileNode = treeViewRoot.Nodes.Add(headerStr);
                                                 treeViewRoot.Expand();

                                                 foreach (var file in group)
                                                 {
                                                     textBoxLog.WriteToLog(file.Path, Utility.MessageType.SubSubMain);

                                                     var treeNode = new TreeNode(file.Path)
                                                     {
                                                         Checked = file != firstFile,
                                                         ToolTipText = Localization.DoubleClickToOpen,
                                                         Tag = file //Дабы знать, что данный чекбокс несет в себе файл
                                                     };

                                                     repeatFileNode.Nodes.Add(treeNode);
                                                     repeatFileNode.Expand();
                                                 }
                                             }));
            }
        }

        private void ButtonSelectFolderClick(object sender, EventArgs e)
        {
            folderBrowserDialog.ShowDialog();
            if (folderBrowserDialog.SelectedPath == string.Empty)
            {
                buttonFind.Enabled = false;
                MessageBox.Show(Localization.ErrorFolderNotSelected);
            }
            else
            {
                buttonFind.Enabled = true;
                textBoxFolderName.Text = folderBrowserDialog.SelectedPath;
            }
        }

        private bool _isSearchMode = true;
        private bool _stop = false;

        private void ButtonFindClick(object sender, EventArgs e)
        {
            if (_isSearchMode)
            {
                _isSearchMode = _stop = buttonSelectFolder.Enabled = buttonDelete.Enabled = treeView.Enabled = false;
                buttonFind.Text = Localization.FindButtonTextOnProgress;

                //Сброс
                treeView.Nodes.Clear();

                //Создаем корень узла
                treeViewRoot = treeView.Nodes.Add(Localization.CheckUncheckAll);

                textBoxLog.WriteToLog(Localization.AnalyzeSizeProgressStart, Utility.MessageType.Main);

                //Создаем асинхронный вызов операции поиска файлов во вторичном потоке
                new AnalyzeSizeDelegeate(Analyze).BeginInvoke(folderBrowserDialog.SelectedPath,
                    textBoxExtensions.Text,
                    comboBox1.SelectedItem == null ? null : comboBox1.SelectedItem.ToString(),
                    FindOperationComplete,
                    null);
            }
            else
            {
                _isSearchMode = _stop = buttonSelectFolder.Enabled = buttonDelete.Enabled = treeView.Enabled = true;
                buttonFind.Text = Localization.FindButtonText;
            }
        }

        private void FindOperationComplete(IAsyncResult iar)
        {
            //Получить результаты вторичного потока, когда будем готовы
            //TODO проверить действительно ли нужен данный кусок кода
            var ar = (AsyncResult) iar;
            var analyzeSizeDelegeate = (AnalyzeSizeDelegeate) ar.AsyncDelegate;
            analyzeSizeDelegeate.EndInvoke(iar);
            

            Invoke(new MethodInvoker(OnFindOperationCompleted));
        }

        private void OnFindOperationCompleted()
        {
            textBoxLog.WriteToLog(Localization.AnalyzeSizeProgressEnd, Utility.MessageType.Main);
            buttonSelectFolder.Enabled = buttonFind.Enabled = buttonDelete.Enabled = treeView.Enabled = true;
            buttonFind.Text = Localization.FindButtonText;
        }

        private void ButtonClearLogClick(object sender, EventArgs e)
        {
            textBoxLog.Clear();
        }

        private void ButtonDeleteClick(object sender, EventArgs e)
        {
            buttonDelete.Enabled = false;

            if (MessageBox.Show(Localization.DoDelete, Localization.DoDeleteOperation, MessageBoxButtons.OKCancel)
                ==
                DialogResult.OK)
            {
                var checkedNodes =
                    GetCheckedItems().Where(x => x.Tag != null && x.Tag.GetType() == typeof (LocalFile));
                var deletedBytes = checkedNodes.Select(x => x.Tag).OfType<LocalFile>().Sum(p => p.Size);
                var deletedCount = checkedNodes.LongCount();

                while (checkedNodes.Any())
                {
                    var checkedNode = checkedNodes.First();

                    var f = (LocalFile) (checkedNode.Tag);

                    try
                    {
                        DirectoryListing.DeleteFile(f.Path, checkBoxRemoveToRecycle.Checked);
                    }
                    catch (Exception exception)
                    {
                        MessageBox.Show(String.Format(Localization.DoDeleteError, f.Path, exception.Message));
                    }
                    finally
                    {
                        checkedNode.Remove();
                    }
                }

                MessageBox.Show(String.Format(Localization.DoDeleteOk, deletedCount, deletedBytes.ToFileSize()));
            }

            buttonDelete.Enabled = true;
        }

        private void TreeViewAfterCheck(object sender, TreeViewEventArgs e)
        {
            // Ставим или убираем отметку со всех подузлов.
            foreach (TreeNode treeSubNode in e.Node.Nodes)
            {
                treeSubNode.Checked = e.Node.Checked;
            }
        }

        private void MouseEnterOnStatus(object sender, EventArgs e)
        {
            CalculateSeletedFilesInfo();
        }

        private void TreeViewDoubleClick(object sender, EventArgs e)
        {
            RunApplication(sender);
        }

        private static void RunApplication(object sender)
        {
            TreeNode selectedNode = ((TreeView) sender).SelectedNode;

            if (selectedNode == null || selectedNode.Tag == null ||
                selectedNode.Tag.GetType() != typeof (LocalFile)) return;

            //Если кликнули по файлу
            Process.Start(((LocalFile) selectedNode.Tag).Path);
        }

        private void CalculateSeletedFilesInfo()
        {
            var checkedNodes = GetCheckedItems()
                .Select(x => x.Tag)
                .OfType<LocalFile>();

            var sumSizeOfSelectedFiles = checkedNodes.Sum(p => p.Size);
            var countOfSelectedFiles = checkedNodes.LongCount();

            toolStripStatusLabel1.Text =
                string.Format(Localization.StatusFilesSelected, countOfSelectedFiles,
                              sumSizeOfSelectedFiles.ToFileSize());
        }

        private IEnumerable<TreeNode> GetCheckedItems()
        {
            return treeView.Nodes.OfType<TreeNode>()
                .SelectMany(x => x.Nodes.OfType<TreeNode>())
                .SelectMany(x => x.Nodes.OfType<TreeNode>())
                .Where(x => x.Checked);
        }

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            var comboBox = sender as ComboBox;
            if (comboBox != null)
            {
                AddOrUpdateAppSettings("AnalyzeSizeAlghoritm", comboBox.SelectedItem.ToString());
            }
        }

        public static void AddOrUpdateAppSettings(string key, string value)
        {
            try
            {
                var configFile = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                var settings = configFile.AppSettings.Settings;
                if (settings[key] == null)
                {
                    settings.Add(key, value);
                }
                else
                {
                    settings[key].Value = value;
                }
                configFile.Save(ConfigurationSaveMode.Modified);
                ConfigurationManager.RefreshSection(configFile.AppSettings.SectionInformation.Name);
            }
            catch (ConfigurationErrorsException)
            {
                Console.WriteLine("Error writing app settings");
            }
        }
    }
}